package com.mia.craftstudio.minecraft.client;

import com.mia.craftstudio.libgdx.Vector3;
import com.mia.craftstudio.minecraft.CraftStudioModelWrapper;
import com.mia.craftstudio.minecraft.CraftStudioRendererVBO;
import com.mia.craftstudio.minecraft.ModelMetadata;
import com.mia.craftstudio.minecraft.forge.CSLibMod;
import com.mia.props.client.renderers.RenderPropInv;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.nio.FloatBuffer;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.entity.player.EntityPlayer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15;

import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBindTexture;

public class CSClientModelWrapperVBO extends CraftStudioModelWrapper {
   public Set topRenderers = new HashSet();
   protected CSClientModelWrapperVBO.RenderingObject roDefault;
   protected int glTextureId = -1;
   protected boolean isPrecomputed = false;
   protected static final int nVertices = 30;
   protected static final int sizeStride = 11;
   protected static final int sizeFloat = 4;

   public CSClientModelWrapperVBO(ModelMetadata metadata) {
      super(metadata);
   }

   public int getGlTextureId() {
      if (this.glTextureId == -1) {
         this.glTextureId = TextureUtil.glGenTextures();
      }

      return this.glTextureId;
   }

   public void deleteGlTexture() {
      if (this.glTextureId != -1) {
         TextureUtil.deleteTexture(this.glTextureId);
         this.glTextureId = -1;
      }

   }

   public void bindGlTexture() {
      glBindTexture(3553, this.getGlTextureId());
   }

   public void addRenderer(CraftStudioRendererVBO renderer) {
      this.topRenderers.add(renderer);
   }

   public void renderPlacement(EntityPlayer player, float timeSinceLastRender, boolean canPlace, int tx, int ty, int tz, int playerOrientation) {
      double px = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)timeSinceLastRender;
      double py = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)timeSinceLastRender;
      double pz = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)timeSinceLastRender;
      GL11.glPushMatrix();
      GL11.glTranslated((double)tx - px + 0.5D, (double)ty - py, (double)tz - pz + 0.5D);
      GL11.glRotatef((float)playerOrientation * 22.5F, 0.0F, -1.0F, 0.0F);
      GL11.glTranslatef(-0.5F, 0.0F, -0.5F);
      this.startOutlineRendering(3.0F, 1.0F, canPlace ? 1.0F : 0.0F, canPlace ? 1.0F : 0.0F, 0.5F);
      this.renderOutlineVBO(this.roDefault, (Object)null, timeSinceLastRender);
      this.stopOutlineRendering();
      GL11.glPopMatrix();
   }

   protected void renderOutline(CSClientModelWrapperVBO.RenderingObject obj, Object instanceRenderState, float timeSinceLastRender) {
      GL11.glPushMatrix();
      this.startOutlineRendering(1.0F, 0.0F, 0.0F, 0.0F, 1.0F);
      this.renderOutlineVBO(obj, instanceRenderState, timeSinceLastRender);
      this.stopOutlineRendering();
      GL11.glPopMatrix();
   }

   protected void activateTransparency() {
      GL11.glEnable(3042);
      GL11.glBlendFunc(770, 771);
   }

   protected void deactivateTransparency() {
      GL11.glDisable(3042);
      GL11.glDisable(2977);
   }

   protected void precompute() {
      Set renderers1st = new HashSet();
      Set renderers2nd = new HashSet();
      Iterator var3 = this.topRenderers.iterator();

      while(var3.hasNext()) {
         CraftStudioRendererVBO renderer = (CraftStudioRendererVBO)var3.next();
         renderer.collectRenderers(renderers1st, renderers2nd);
      }

      this.roDefault = new CSClientModelWrapperVBO.RenderingObject(this.generateVBOs(renderers1st, renderers2nd), new int[]{renderers1st.size(), renderers2nd.size()});
      this.isPrecomputed = true;
   }

   public void render(Object instanceRenderState, float timeSinceLastRender, int pass, boolean renderOutline, float rotationAngle, Vector3 rotationVector, Vector3 preRotataionTranslation, Vector3 postRotataionTranslation) {
      if (!this.isPrecomputed) {
         this.precompute();
      }
      GL11.glEnable(GL12.GL_RESCALE_NORMAL);

      GL11.glPushMatrix();
      if (!Vector3.Zero.equals(preRotataionTranslation)) {
         GL11.glTranslatef(preRotataionTranslation.x, preRotataionTranslation.y, preRotataionTranslation.z);
      }

      if (!Vector3.Zero.equals(rotationVector)) {
         GL11.glRotatef(rotationAngle, rotationVector.x, rotationVector.y, rotationVector.z);
      }

      if (!Vector3.Zero.equals(postRotataionTranslation)) {
         GL11.glTranslatef(postRotataionTranslation.x, postRotataionTranslation.y, postRotataionTranslation.z);
      }

      if (CSLibMod.displayOutline && renderOutline) {
         this.renderOutline(this.roDefault, instanceRenderState, timeSinceLastRender);
      }

      this.renderWithVBO(this.roDefault, instanceRenderState, timeSinceLastRender, pass);
      GL11.glPopMatrix();
   }

   protected int[] generateVBOs(Set nodes1st, Set nodes2nd) {
      FloatBuffer qBuffer1st = BufferUtils.createFloatBuffer(330 * nodes1st.size());
      FloatBuffer qBuffer2nd = BufferUtils.createFloatBuffer(330 * nodes2nd.size());
      FloatBuffer outlineBuffer = BufferUtils.createFloatBuffer(60 * (nodes1st.size() + nodes2nd.size()));
      Iterator var6 = nodes1st.iterator();

      CraftStudioRendererVBO renderer;
      while(var6.hasNext()) {
         renderer = (CraftStudioRendererVBO)var6.next();
         renderer.generateVBOTriangleStripInterleaved(qBuffer1st, 0);
         renderer.generateVBOLineStrip(outlineBuffer, 0);
      }

      var6 = nodes2nd.iterator();

      while(var6.hasNext()) {
         renderer = (CraftStudioRendererVBO)var6.next();
         renderer.generateVBOTriangleStripInterleaved(qBuffer2nd, 0);
         renderer.generateVBOLineStrip(outlineBuffer, 0);
      }

      qBuffer1st.flip();
      qBuffer2nd.flip();
      outlineBuffer.flip();
      int VBO1st = GL15.glGenBuffers();
      int VBO2nd = GL15.glGenBuffers();
      int VBOOutline = GL15.glGenBuffers();
      GL15.glBindBuffer(34962, VBO1st);
      GL15.glBufferData(34962, qBuffer1st, 35044);
      GL15.glBindBuffer(34962, VBO2nd);
      GL15.glBufferData(34962, qBuffer2nd, 35044);
      GL15.glBindBuffer(34962, VBOOutline);
      GL15.glBufferData(34962, outlineBuffer, 35044);
      GL15.glBindBuffer(34962, 0);
      return new int[]{VBO1st, VBO2nd, VBOOutline};
   }

   protected void renderWithVBO(CSClientModelWrapperVBO.RenderingObject obj, Object instanceRenderState, float timeSinceLastRender, int pass) {
      this.bindGlTexture();
      boolean CULL_FACE = GL11.glIsEnabled(2884);
      GL11.glEnable(2884);
      boolean LIGHT1 = GL11.glIsEnabled(16385);
      GL11.glDisable(16385);
      int[] vbos = obj.getVBOs();
      int[] sizes = obj.getSizes();
      GL11.glEnableClientState(32884);
      GL11.glEnableClientState(32888);
      GL11.glEnableClientState(32885);
      GL11.glEnableClientState(32886);

//      System.out.println(instanceRenderState == null ? "Inventory " + this.getGlTextureId() : "World " + this.getGlTextureId());
//      RenderPropInv.dumpAllIsEnabled(instanceRenderState == null ? "Inventory " : "World ");
//
//      glBindTexture(GL_TEXTURE_2D,this.getGlTextureId());

      if (pass == 0 || pass == 2) {
         GL15.glBindBuffer(34962, vbos[0]);
         GL11.glVertexPointer(3, 5126, 44, 0L);
         GL11.glNormalPointer(5126, 44, 12L);
         GL11.glTexCoordPointer(2, 5126, 44, 24L);
         GL11.glColorPointer(3, 5126, 44, 32L);
         GL11.glDrawArrays(5, 0, 30 * sizes[0]);
      }

      if ((pass == 1 || pass == 2) && sizes[1] > 0) {
         this.activateTransparency();
         GL15.glBindBuffer(34962, vbos[1]);
         GL11.glVertexPointer(3, 5126, 44, 0L);
         GL11.glNormalPointer(5126, 44, 12L);
         GL11.glTexCoordPointer(2, 5126, 44, 24L);
         GL11.glColorPointer(3, 5126, 44, 32L);
         GL11.glDrawArrays(5, 0, 30 * sizes[1]);
         this.deactivateTransparency();
      }

      GL15.glBindBuffer(34962, 0);
      GL11.glDisableClientState(32885);
      GL11.glDisableClientState(32888);
      GL11.glDisableClientState(32884);
      GL11.glDisableClientState(32886);
      if (LIGHT1) {
         GL11.glEnable(16385);
      }

      if (!CULL_FACE) {
         GL11.glDisable(2884);
      }

   }

   protected void renderOutlineVBO(CSClientModelWrapperVBO.RenderingObject obj, Object instanceRenderState, float timeSinceLastRender) {
      GL15.glBindBuffer(34962, obj.getVBOOut());
      GL11.glVertexPointer(3, 5126, 0, 0L);

      for(int i = 0; i < obj.getSizeOut() * 4; ++i) {
         GL11.glDrawArrays(3, i * 5, 5);
      }

   }

   @SideOnly(Side.CLIENT)
   protected void startOutlineRendering(float width, float r, float g, float b, float a) {
      GL11.glEnable(3042);
      OpenGlHelper.glBlendFunc(770, 771, 1, 0);
      GL11.glLineWidth(width);
      GL11.glColor4f(r, g, b, a);
      GL11.glDisable(3553);
      GL11.glEnableClientState(32884);
   }

   @SideOnly(Side.CLIENT)
   protected void stopOutlineRendering() {
      GL11.glDisableClientState(32884);
      GL15.glBindBuffer(34962, 0);
      GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
      GL11.glEnable(3553);
      GL11.glDisable(3042);
   }

   protected class RenderingObject {
      private final int[][] vbos = new int[16][];
      private final int[][] elems = new int[16][];

      public RenderingObject(int[] vbos, int[] sizes) {
         this.vbos[0] = vbos;
         this.elems[0] = sizes;
      }

      public RenderingObject(int[] vbos, int[] sizes, int meta) {
         this.vbos[meta] = vbos;
         this.elems[meta] = sizes;
      }

      public int[] getVBOs() {
         return this.vbos[0];
      }

      public int getVBO1st() {
         return this.vbos[0][0];
      }

      public int getVBO2nd() {
         return this.vbos[0][1];
      }

      public int getVBOOut() {
         return this.vbos[0][2];
      }

      public int[] getSizes() {
         return this.elems[0];
      }

      public int getSize1st() {
         return this.elems[0][0];
      }

      public int getSize2nd() {
         return this.elems[0][1];
      }

      public int getSizeOut() {
         return this.elems[0][0] + this.elems[0][1];
      }

      public int[] getVBOs(int meta) {
         return this.vbos[meta];
      }

      public int getVBO1st(int meta) {
         return this.vbos[meta][0];
      }

      public int getVBO2nd(int meta) {
         return this.vbos[meta][1];
      }

      public int getVBOOut(int meta) {
         return this.vbos[meta][2];
      }

      public int[] getSizes(int meta) {
         return this.elems[meta];
      }

      public int getSize1st(int meta) {
         return this.elems[meta][0];
      }

      public int getSize2nd(int meta) {
         return this.elems[meta][1];
      }

      public int getSizeOut(int meta) {
         return this.elems[meta][0] + this.elems[meta][1];
      }
   }
}
